#!/usr/bin/env python
# -*- coding: utf-8 -*-
# Purpose: 
# Created: 07/06/2009
# $Id $
# Copyright (c) 2008,2009 by University College London
# Authors: 
#  Didrik Pinte --<dpinte@dipole-consulting.com>
#
# This program is free software under the GPL (>=v2)
# Read the file COPYING coming with HydroPlatform for details.

import logging

from hydroplatform.model import network
from hydroplatform.model.fields import Parameter, TimeSerie, Table, \
     SeasonalParameter, NodeReferences, LinkReferences, TableCol

from hydroplatform.model import units


Column =  TableCol

################ DESCRIPTIONS ############################

default_description = u""" This is mostly used for network connections. Elevation should be defined if Pumping or Hydropower is defined on a link connected to this link or if a bidirectional link is connected to it.
"""

reservoir_description = u"""Want to put Hydropower and Pump data into other data groups

Seepage in Rating table should be in units of hm3 per time step, or unit conversion should turn it into that

If pump or hydropower data is entered, the KeyHydropower input in the INP file should be turned on

Reservoir balance function has (3x number of reservoirs in group) columns
"""


consumption_description = u"""Consumption nodes model sites where water is used consumptively and hence taken out of the system.  For each consumption node the user defines a consumption function that describes how much of the node’s water normally destined for outflow (total water coming out of the node less any water that is to be directed to demand links) is consumed. These functions are entered in tables as total consumption in flow units as a function of the total water available for release from of the node. 

Any node that does not have any allocation functions defined (which are defined on Diversion and Demand/Diversion links), or that is not at the end of a bidirectional link (surface or groundwater), can be a consumption node. 

Elevation should be entered only if the consumption node is connected to a link with Pumping and/or Hydropower defined.

Consumption nodes can also be a gauge nodes.
"""

demand_consumption_description = u"This node combines the function of Demand and Consumption nodes into one. See those nodes' description for details."

demand_description = u"""A node can be designated a demand node if it has a flow requirement (flow units). Each demand node can have one or more designated source nodes that can release additional water to fulfill the demand target. These are defined in the Source(s) table.

Only reservoirs can be source nodes, other storage nodes (such as aquifers) and non-storage nodes cannot be designated as sources. 
Each source node has a corresponding multiplier. This multiplier serves to establish for how much of a given demand each source node is responsible. For example, a demand node can have source 1 and source 2 as its sources with 0.7 and 0.3 as their multipliers respectively. If the demand node experiences a demand deficit (this occurs when the flow or 14 storage target is not met), source 1 will attempt to release 70% of the deficit while source 2 will release 15 only 30%.

Source node multipliers can be greater than one or add up to a value greater than one if a greater value  of water should be released than the demand calls for. This can be beneficial if some of the water released is redirected or lost from the system through evaporation or seepage on its way to the demand site. 

Demand nodes can also have a carryover fraction defined which designates how much of any demand  deficit carries over to the next sub-time step. 

Elevation should be entered only if the demand node has any bidirectional or links with Power and/or Pumping enabled. 



Demand nodes can be gauge nodes. At the beginning of each sub-time step only water input from gauge inflow contributes to the reduction of the Target demand. Any inflow that results from upstream flow does not contribute to deficit reduction and thus the demand node will still ask for whatever amount of Target demand is left after the gauge inflow (if any) is considered (see manual for more detailed explanation)

Demand nodes could have a demand node connected directly upstream for demand based diversion from a node directly upstream.
"""
lake_description = u"""Natural lakes are surface water storage nodes. The user must define its storage capacity [volume units]  and initial volume as well as an outflow [flow units] vs. node storage [volume units] rating table. The release from the lake during simulation is determined by the interpolation of this table. Evaporation and seepage losses can be defined for lakes.

If evaporation is to be considered, an evaporation rate must be defined along with a surface area vs. storage rating table. A negative loss rate can be used to represent  rainfall if desired. Seepage is defined as a seepage rate [flow units] vs. storage rating table and this rate an be negative if seepage is entering the lake rather than leaving it.

If hydropower or pumping is to be enabled on a link exiting the lake, an elevation vs. storage rating table must be defined.

Gauge, Consumption and Target (volume units) storage inputs can also be applied to lakes. See relavent node for input information.
"""


flow_input_description = u"""Gauge node with inflow time-series

Elevation should be defined if there are incoming links with Power/Pumping activated or if bidirectional links 
"""

aquifer_description = u"""Aquifers are storage nodes and as such should have an initial storage volume defined. They do not have a maximum capacity. Flows to and from aquifers depend on the current water elevation in the aquifer and the node connected to the aquifer. For this reason an elevation vs. storage rating table must be defined for the aquifer. A seepage loss table can also be defined at aquifer nodes analogous to other storage nodes. Aquifer recharge from water outside the system can be simply  modeled by designating the aquifer a gauge node (Flow Input). All links with an aquifer at one or both ends must be groundwater links.

Aquifers can connect to:
1. Surface storage nodes
2. Surface links (through a connection node). However, routing must be enabled on at least one of the incoming uni-directional links connected to the connection node.
3. Another verticle aquifer if given aquifer is verticle
4. Another horizontal aquifer if given aquifer is horizontal 
"""


wetland_description = u"""Wetlands are similarly defined to Aquifers and require the same data inputs. Evaporation can additionally be defined on wetlands and thus a surface area vs. storage volume rating table and evaporation rate can be defined. Only bidirectional links should be connected to Wetlands.
"""

################ CODE ############################

custom_step_code = """# import the relativedelta module
from dateutil.relativedelta import relativedelta

# set the starting date
date1 = self.starting_date
# compute the first season end date
date2 = date1 + relativedelta(days=150)
# compute the second season end date
date3 = date1 + relativedelta(years=1)
# add the ending date
date4 = self.tod

# group the dates in the result variable
self.result = [date1, date2, date3, date4]
"""

################ NODE TYPES ############################


def is_iras_project(project):
    """
    Method should check if the project is IRAS enabled or not.
    
    At the moment, it does only check if all the node and link types
    does exist. The moment could be improved by verifying the fields
    
    FIXME : node and link type names should have a single definition
    """
    node_types = [u"Consumption", u"Demand/Consumption", u"Demand", u"Lake",
                     u"Aquifer", u"Default node", u"Flow input", u"Wetland", 
                     u"Reservoir"] 
    existing_ntypes = [n.name for n in project.nodetypes]
    for ntype in node_types:
        if ntype not in existing_ntypes:
            logging.warn("Node type %s is missing" % ntype)
            return False        
    link_types = [u"Demand/Diversion Link", u"Unidirectional Link", 
                  u"Demand Link", u"Groundwater Link", 
                  u"Surface Bidirectional Link", u"Diversion Link"] 
    existing_ltypes = [l.name for l in project.linktypes]
    for ltype in link_types:
        if ltype not in existing_ltypes:
            logging.warn("Link type %s is missing" % ltype)
            return False
        
    return True
       
    

def get_node_types(project):
    node_key_names = [u"name", u"description", u"size", u"shape", u"pen", u"brush"]
    
    
    default_node_fields = [Parameter(name=u"Elevation" , unit="m", format="float")]
    
    flow_factor_table = Table(name=u"Flow Factors", columns=[Column(name=u"Run", format="integer"),
                                                       Column(name=u"Jan" ),
                                                       Column(name=u"Feb" ),
                                                       Column(name=u"Mar" ),
                                                       Column(name=u"Apr" ),
                                                       Column(name=u"Jun" ),
                                                       Column(name=u"Jul" ),
                                                       Column(name=u"Aug" ),
                                                       Column(name=u"Sep" ),
                                                       Column(name=u"Oct" ),
                                                       Column(name=u"Nov" ),
                                                       Column(name=u"Dec" )])
    # Field lists contains a tuple (field_type, field_group_name)       
    consumption_fields = [ 
            Parameter(name=u"Elevation", unit='m'),   
            Table(name=u"Consumption Function", columns=[Column(name=u"Total Output of Node", unit="m3/s"),
                                                       Column(name=u"Consumption", unit="m3/s")]),   
            [TimeSerie(name=u"Flow Input" , unit='m3/s', format='float', 
                       fromd=project.horizon_from, tod=project.horizon_to,
                       step=project.time_step), u"Flow Input"],   
            [Parameter(name=u"Flow Factors Available?" , unit='', format='binary'), u"Flow Factors"],           
            [flow_factor_table, u"Flow Factors"]
    ]
    
    
    demand_consumption_fields = [ 
            Parameter(name=u"Elevation", unit='m'),   
            Table(name=u"Consumption Function", columns=[Column(name=u"Total Output of Node", unit="m3/s"),
                                                       Column(name=u"Consumption", unit="m3/s")]),   
            [TimeSerie(name=u"Flow Input", unit='m3/s', 
                       fromd=project.horizon_from, tod=project.horizon_to,
                       step=project.time_step), u"Flow Input"],   
            [Parameter(name=u"Flow Factors Available?", unit='', format="binary"), u"Flow Factors"],           
            [flow_factor_table, u"Flow Factors"],
            SeasonalParameter(name=u"Target Flow", 
                               unit=u"m3/s", 
                               fromd=project.horizon_from, 
                               tod = project.horizon_to,
                               step = units.YEARLY,
                               horizon=True, 
                               times=1
                               ),
            TimeSerie(name=u"Demand Time Series", unit='m3/s', 
                       fromd=project.horizon_from, tod=project.horizon_to,
                       step=project.time_step),              
			Parameter(name=u"Carry Over", unit=''),
			Parameter(name=u"Annual Demand Growth", unit='-',format="float"),			
			Parameter(name=u"Do not consider passive water?", unit='-',format="binary"),
            Table(name=u"Source(s)", columns=[Column(name=u"Source ID", unit="-",format="integer"),
                                             Column(name=u"Source Type (1-Node, 2-Link)", unit="-",format="integer"),
											 Column(name=u"Contribution Fraction", unit="-"),
                                             Column(name=u"Max Flow per Time-Step", unit="float"), 
                                             Column(name=u"Max Flow per Year (*365 days)", unit="float"),
                                             Column(name=u"Propogating Link", unit="-",format="integer")
                                             ]),
            Parameter(name=u"Enable source priorities?", unit='-',format="binary"),                                 
			[Table(name=u"Demand Reduction Table", columns=[Column(name=u"Threshold Storage (fraction)", unit="-"),
                                              Column(name=u"Demand Reduction (fraction)", unit="-")
                                              ]), u"Demand Reduction"], 
            [Parameter(name=u"Linked Storage Node", unit='-',format="integer"), u"Demand Reduction"]   			
    ]
    
    demand_fields = [ 
            Parameter(name=u"Elevation", unit='m'),   
            [TimeSerie(name=u"Flow Input", unit='m3/s', 
                       fromd=project.horizon_from, tod=project.horizon_to,
                       step=project.time_step), u"Flow Input"],   
            [Parameter(name=u"Flow Factors Available?", unit='', format="binary"), u"Flow Factors"],           
            [flow_factor_table, u"Flow Factors"],
            SeasonalParameter(name=u"Target Flow", unit="m3/s",  
                               fromd=project.horizon_from, horizon=True, 
                               tod = project.horizon_to,
                               step= units.YEARLY, times=1
                               ),
            TimeSerie(name=u"Demand Time Series", unit='m3/s', 
                       fromd=project.horizon_from, tod=project.horizon_to,
                       step=project.time_step),  							   
            Parameter(name=u"Carry Over", unit=''),
			Parameter(name=u"Annual Demand Growth", unit='-',format="float"),			
			Parameter(name=u"Do not consider passive water?", unit='-',format="binary"),
            Table(name=u"Source(s)", columns=[Column(name=u"Source ID", unit="-",format="integer"),
                                             Column(name=u"Source Type (1-Node, 2-Link)", unit="-",format="integer"),
											 Column(name=u"Contribution Fraction", unit="-"),
                                             Column(name=u"Max Flow per Time-Step", unit="float"),
                                             Column(name=u"Max Flow per Year (*365 days)", unit="float"),
                                             Column(name=u"Propogating Link", unit="-", format="integer")
                                             ]),
            Parameter(name=u"Enable source priorities?", unit='-',format="binary"), 			
            [Table(name=u"Demand Reduction Table", columns=[Column(name=u"Threshold Storage (fraction)", unit="-"),
                                             Column(name=u"Demand Reduction (fraction)", unit="-")
                                             ]), u"Demand Reduction"], 
            [Parameter(name=u"Linked Storage Node", unit='-',format="integer"), u"Demand Reduction"]  			
 
											 
    ]

    aquifer_fields = [
            Parameter(name=u"Initial Storage", unit='hm3', ),           
            Table(name=u"Rating Table", columns=[Column(name=u"Elevation", unit="m"),
                                                Column(name=u"Area", unit="ha"),
                                                Column(name=u"Volume", unit="hm3"),
                                                Column(name=u"Seepage", unit="m3/s")
                                                ]),   
            [TimeSerie(name=u"Flow Input",  unit='m3/s', 
                       fromd=project.horizon_from, tod=project.horizon_to,
                       step=project.time_step),u"Flow Input"],           
            [Parameter(name=u"Flow Factors Available?", unit='', format="binary"), u"Flow Factors"], 
            [flow_factor_table, u"Flow Factors"]
    ]
    
    flow_input_fields = [        
            [TimeSerie(name=u"Flow Input", unit='m3/s', format='float', 
                       fromd=project.horizon_from, tod=project.horizon_to,
                       step=project.time_step ), u"Flow Input"],
            Parameter(name=u"Elevation", unit='m'),
            [Parameter(name=u"Flow Factors Available?", unit='', format="binary"), u"Flow Factors"],
            [flow_factor_table, u"Flow Factors"]        
    ]
    
    wetland_fields = [
            Table(name=u"Rating Table", 
                   columns=[Column(name=u"Elevation", unit="m"),
                            Column(name=u"Area", unit="ha"),
                            Column(name=u"Volume", unit="hm3"),
                            Column(name=u"Seepage", unit="m3/s")
                            ]),   
            Parameter(name=u"Initial Storage",unit='hm3'),
            [TimeSerie(name=u"Flow Input", unit='m3/s', 
                       fromd=project.horizon_from, tod=project.horizon_to,
                       step=project.time_step), u"Flow Input"],
            SeasonalParameter(name=u"Evaporation/Rainfall Rate", unit="m/day",
                               fromd=project.horizon_from, horizon=False,
                               tod = project.horizon_to,
                               step=units.DAILY, times=1),
            [Parameter(name=u"Flow Factors Available?", unit='', format="binary"),
             u"Flow Factors"],
            [flow_factor_table, u"Flow Factors"]        
    ]
    
    lake_fields = [
            Parameter(name=u"Storage Capacity", unit='hm3'),                   
            Parameter(name=u"Initial Storage", unit='hm3',),     
            SeasonalParameter(name=u"Target Storage", unit='hm3',
                              fromd=project.horizon_from, horizon=True,
                               tod = project.horizon_to,
                               step=units.YEARLY, times=1),
            [TimeSerie(name=u"Flow Input", unit='m3/s', 
                       fromd=project.horizon_from, tod=project.horizon_to,
                       step=project.time_step ), u"Flow Input"], 
            Parameter(name=u"Carry Over", unit=''),
            Table(name=u"Source(s)", 
                   columns=[Column(name=u"Source ID", unit="-", format="integer"),
                            Column(name=u"Contribution Fraction", unit="-"),
                            Column(name=u"Contribution Fraction", unit="-"),
                            Column(name=u"Max Flow per Time-Step", unit="float"),
                            Column(name=u"Propogating Link", unit="-",format="integer"),
                            ]),          
            Table(name=u"Consumption Function", 
                   columns=[Column(name=u"Total Output of Node", unit="m3/s"),
                            Column(name=u"Consumption", unit="m3/s")
                            ]),          
            [Parameter(name=u"Flow Factors Available?", unit='', format="binary"), 
             u"Flow Factors"],
            [flow_factor_table, u"Flow Factors"],
            SeasonalParameter(name=u"Evaporation/Rainfall Rate", unit="m/day", 
                           fromd=project.horizon_from, horizon=True, 
                           tod = project.horizon_to,
                           step=units.MONTHLY, times=1
                           ), 
            Table(name=u"Rating Table", 
                   columns=[Column(name=u"Elevation", unit="m"),
                            Column(name=u"Area", unit="ha"),
                            Column(name=u"Volume", unit="hm3"),
                            Column(name=u"Seepage", unit="m3/s"),
                            Column(name=u"Release", unit="m3/s"),
                            ]),               
    ]
    
    reservoir_fields = [
            Parameter(name=u"Storage Capacity", unit='hm3'), 
            Parameter(name=u"Initial Storage", unit='hm3'),
			Parameter(name=u"Refill Trigger (% fill in Lead Reservoir in group)", unit='-'),			
            [TimeSerie(name=u"Flow Input", unit='m3/s', 
                       fromd=project.horizon_from, tod=project.horizon_to,
                       step=project.time_step),
                       u"Flow Input"],                   
            NodeReferences(name=u"Rule Group ID"),
            Table(name=u"Rating Table", 
                   columns=[Column(name=u"Elevation", unit="m"),
                            Column(name=u"Area", unit="ha"),
                            Column(name=u"Volume", unit="hm3"),
                            Column(name=u"Seepage", unit="m3/s"),
                            Column(name=u"Maximum Release", unit="m3/s"),
                            Column(name=u"Minimum Release", unit="m3/s"),
                            ]),   
            SeasonalParameter(name=u"Evaporation/Rainfall Rate", unit="m/day", 
                               fromd=project.horizon_from, horizon=True, 
                               tod = project.horizon_to,
                               step=units.MONTHLY, times=1
                               ),            
			#3-28-11 Evgenii added performance thresholds, because of how tables work, needed to put in dummy column (cant have only one column)
			[Table(name=u"Performance Thresholds", 
				   columns=[Column(name=u"Failure Threshold (fraction target storage)", unit="-", format="float"),
				            Column(name=u"Dummy column", unit="-"),
                            ]), u"Target"],    
			
			[Table(name=u"Rule Table", 
                   columns=[Column(name=u"Min Beg Storage", unit="hm3"),
                            Column(name=u"Min Beg Release", unit="m3/s"),
                            Column(name=u"Max Beg Storage", unit="hm3"),
                            Column(name=u"Max Beg Release", unit="m3/s"),
                            Column(name=u"Min End Storage", unit="hm3"),
                            Column(name=u"Min End Release", unit="m3/s"),
                            Column(name=u"Max End Storage", unit="hm3"),
                            Column(name=u"Max End Release", unit="m3/s"),
                            ]), u"Reservoir Rules"],          
            #Evgenii modified balance table 3-28-11
			[Table(name=u"Balance Table (for balance on group)", columns=[Column(name=u"Group Storage", unit="hm3" ),
                                                  Column(name=u"Res ID", unit="", format="integer"),
                                                  Column(name=u"Res Balance Storage", unit="hm3"),
                                                  Column(name=u"Res ID", unit="", format="integer"),
                                                  Column(name=u"Res Balance Storage", unit="hm3"),
                                                  Column(name=u"Res ID", unit="", format="integer"),
                                                  Column(name=u"Res Balance Storage", unit="hm3"),
                                                  Column(name=u"Res ID", unit="", format="integer"),
                                                  Column(name=u"Res Balance Storage", unit="hm3"),
                                                  Column(name=u"Res ID", unit="", format="integer"),
                                                  Column(name=u"Res Balance Storage", unit="hm3"),
                                               ]), u"Reservoir Rules"],          
            [Table(name=u"Balance Table (for balance on lead reservoir)", columns=[Column(name=u"Res ID", unit="", format="integer"),                                                 
                                                  Column(name=u"Lead Reservoir Storage", unit="hm3"),
												  Column(name=u"Res Balance Storage", unit="hm3"),
                                                  Column(name=u"Res ID", unit="", format="integer"),
                                                  Column(name=u"Lead Reservoir Storage", unit="hm3"),
												  Column(name=u"Res Balance Storage", unit="hm3"),
                                                  Column(name=u"Res ID", unit="", format="integer"),
                                                  Column(name=u"Lead Reservoir Storage", unit="hm3"),
												  Column(name=u"Res Balance Storage", unit="hm3"),
                                                  Column(name=u"Res ID", unit="", format="integer"),
                                                  Column(name=u"Lead Reservoir Storage", unit="hm3"),
												  Column(name=u"Res Balance Storage", unit="hm3"),
                                                  Column(name=u"Res ID", unit="", format="integer"),
                                                  Column(name=u"Lead Reservoir Storage", unit="hm3"),
												  Column(name=u"Res Balance Storage", unit="hm3"),
                                               ]), u"Reservoir Rules"], 											   
            [SeasonalParameter(name=u"Target Storage" , unit='hm3',
                               fromd=project.horizon_from, horizon=True, 
                               tod = project.horizon_to,
                               step= units.YEARLY, times=1
                               ), u"Target"],
            [Parameter(name=u"Enable source priorities?", unit='-',format="binary"), u"Target"],                                
			[Parameter(name=u"Balance on Group (0) or Lead Reservior Volume(1)" , unit='-',format="binary"), u"Reservoir Rules"],				   
            [Parameter(name=u"Carry Over", unit='-'), u"Reservoir Rules"],
            Table(name=u"Source(s)", columns=[Column(name=u"Source ID", unit="-",format="integer"),
                                             Column(name=u"Source Type (1-Node, 2-Link)", unit="-",format="integer"),
											 Column(name=u"Contribution Fraction", unit="-"),
                                             Column(name=u"Max Flow per Time-Step", unit="float"),
                                             Column(name=u"Max Flow per Year (*365 days)", unit="float"),
                                             Column(name=u"Propogating Link", unit="-",format="integer")
                                             ]),          
            [Table(name=u"Consumption Function", columns=[Column(name=u"Total Output of Node", unit="m3/s" ),
                                                         Column(name=u"Consumption", unit="m3/s" )
                                               ]), u"Consumption"],          
            [Parameter(name=u"Flow Factors Available?" , unit='', format="binary"), 
             u"Flow Factors"],
            [flow_factor_table, u"Flow Factors"]
    ]
    
    node_types = [
        [u"Consumption", consumption_description,
         u"circle", u"#000000", u"#ffff00", 10, consumption_fields],
        [u"Demand/Consumption", demand_consumption_description,
         u"circle", u"#000000", u"#ff8040", 10, demand_consumption_fields],
        [u"Demand", demand_description, 
         u"circle", u"#000000", u"#ff0000", 10, demand_fields],
        [u"Lake", lake_description, 
         u"circle", u"#000000", u"#0000ff", 20, lake_fields],    
        [u"Aquifer", aquifer_description, 
         u"rectangle", u"#000000", u"#008000", 20, aquifer_fields],    
        [u"Default node", default_description, 
         u"rectangle", u"white", u"black", 20, default_node_fields],    
        [u"Flow input", flow_input_description,  
         u"circle", u"#000000", u"#00ff00", 10, flow_input_fields],        
        [u"Wetland", wetland_description, 
         u"rectangle", u"#000000", u"#808000", 20, wetland_fields],
        [u"Reservoir", reservoir_description, 
         u"triangle", u"#000000", u"#0000ff", 20, reservoir_fields]
    ] 
    
    return node_key_names, node_types

################ LINK TYPES ############################
demand_diversion_link_description = u"""This link type combines the functions of Demand and Diversion links. Please refer to those links for information on this link type inputs."""


unidirectional_link_description = u"""Unidirectional surface links connect surface nodes and can allow flow only in one direction. These link types do not have any required definitions. However, loss functions and routing can be defined. Loss in links can take the form of evaporation of seepage. IRAS-2010 has three loss calculation methods  of which only one can be used on each link. 

Loss method 1 uses a user defined simple loss [flow units] as a function of link discharge table. This can represent seepage and evaporation or a combination of both. Loss can also be negative to represent rainfall or positive seepage. 

Loss method 2 and 3 calculate the loss using an evaporation rate and a calculated link area. To calculate the link area, the link length and width are needed. The link length is user defined and the link width is determined differently for loss methods 2 and 3. 

The link length is user defined and the link width is determined differently for loss methods 2 and 3. Method 2 obtains the link width from a user-defined link width as a function of link flow table. Loss method 3 requires additional information on the link cross section geometry to calculate the link width at the appropriate link flow rate. This information includes the link’s base width, depth and the upper and lower slope angles of the right and left link banks.

To use method 3, routing must be enabled on the link. 
If the residence time in a unidirectional link is longer than the sub-time step, link routing should be  enabled. When routing is implemented, the flow into a link doesn’t necessary equal flow out of the link,  since the water exiting the link is from previous sub-time steps. IRAS-2010 uses one of two methods for routing.

Please see manual for routing method descriptions.

Pumping and Hydropower can be defined on unidirectional links. 

Links with hydropower require the following data: 
* The plant capacity at the rated head, ( [kWh]) 
* The fraction of the time the plant will operate (the Plant Factor, PF) 
* The turbine elevation 
* The minimum flow rate through the plant to produce energy 
* The plant efficiency 

The nodes at both ends of the hydropower link require elevation data. If they are storage nodes they require an elevation rating table. Non-storage nodes require only a node elevation.

Links with pumps require the pump efficiency.
"""
demand_link_description = u"""If the downstream end of a unidirectional link is a demand node, the link can be additionally defined as a demand link. Designating a link as a demand link gives it priority over all other non-demand links  originating from the node. Water will first be allocated to demand nodes before any other unidirectional links. Subject to the total available water, the amount allocated to demand links is the current sub-time  step demand deficit in the downstream demand node. 

All general Unidirectional fields described in that link type can also be defined for demand links.
"""

groundwater_link_description = u"""
Any link that connects to an aquifer is a groundwater link. All groundwater links are bidirectional. The data required for the groundwater link depends on what kind of node the link connects to the aquifer and which ground water flow calculation method is to be used. 

Groundwater links can connect aquifers to river or stream reaches, storage nodes or other aquifers. If the link connects to another aquifer it must be determined if the aquifer forms a horizontal or vertical layer with the first aquifer. Flow in the groundwater link can be calculated using five different methods. The first and most general method requires the definition of a flow matrix which allows the interpolation of flow in the link depending on the storage or available water for release in the connecting nodes. This method can be used for flow between the aquifer and another aquifer, a surface storage node or a river reach. For this method, elevation data is not required. The total real-time storage of a node is used for the interpolation if the node is a storage node while the available water, the water available in the node for release, is used when the node is a non-storage node. The flow matrix is determined by the user and should take into account the rating tables of the storage nodes or links. Both positive and negative flows can be entered in the flow matrix, with positive flows designating flow in the “to node” direction while negative flows designate flows in the “from 14 node” direction.

The remaining calculation methods for groundwater flow depend on the type of node that is connected to the ground water link and can be used in the place of the general flow matrix method. All of these methods follow Darcy flow laws and require the following data:

* The hydraulic conductivity constant K
* Link length or surface area in the case of vertical aquifers
* Link width
* Link elevation.

The direction and magnitude of groundwater flow depends on the head difference between the two 1 connected nodes and so elevation data is required for both nodes. In the case of storage nodes (including the aquifer), the elevation data is interpolated from elevation rating tables.

Elevation data for non-storage nodes located on river/stream reaches is more complicated. 
The elevation data for these nodes is equal to the base node depth plus the highest depth of the incoming unidirectional links. Depth calculations only happen if a link has routing and loss method 3 enabled. This means that the groundwater flow to a node on a reach can only be calculated if at least one of the other incoming links to the node has loss method 3 and routing activated. Ground water links can have pump data defined if groundwater is to be pumped out of the aquifer. Loss cannot be defined on groundwater links.
"""
surface_bidirectional_link_description = u"""Surface links can also be designated bidirectional. Flow within these nodes can only be calculated using 12 the flow matrix method. They can have hydropower and pumping defined. All links connected to wetlands are bidirectional. """


diversion_link_description = u"""If a unidirectional link is a non-natural link such as a canal or pipeline it should additionally be designated as a diversion link and must have a maximum flow rate capacity defined. Additionally, all diversion links require an allocation function. This is the case even if the diversion link is the only link coming out of a node. Currently only one diversion link can originate from a given node."""


def get_link_types(project):
    link_key_names = ["name", "description", "size", "pen", "style"]

    
    
    demand_diversion_link_fields = [
		Parameter(name="Price per kWh", unit='-'),         
		[Parameter(name="Initial Storage 1", unit='hm3'), 
        "Routing 1"],
        [Parameter(name="Initial Storage 2", unit='hm3'), 
        "Routing 2"],
        [Parameter(name="Number of Cascading Reservoirs", unit='-', format="integer"), 
        "Routing 2"],
        [Parameter(name="a (inflow parameter)", unit='-'), "Routing 2"],
        [Parameter(name="b (volume parameter)", unit='-'), "Routing 2"],
        [Parameter(name="c (exponent)", unit='-'), "Routing 2"],
        [Parameter(name="Detention Storage", unit='hm3'), 
        "Routing 1"], 
        [Parameter(name="Linear Parameter", unit='-'), 
        "Routing 1"],  
        [Parameter(name="Exponent", unit='-'), 
        "Routing 1"],  
        [Table(name="Loss Table", columns=[Column(name="Flow Through Link", unit="m3/s"),
                                           Column(name="Loss", unit="m3/s")]), "Loss 1"],        
		[Parameter(name="Length 2", unit='m'), 
        "Loss 2"],
        [Parameter(name="Length 3", unit='m'), 
        "Loss 3"], 		
        Parameter(name="kWh per m3", unit='-'), 
        [SeasonalParameter(name="Loss Rate 2", unit='m/day', 
                           fromd=project.horizon_from, 
                           tod=project.horizon_to, step=units.MONTHLY, times=1), 
        "Loss 2"],    
        [SeasonalParameter(name="Loss Rate 3", unit='m/day', 
                           fromd=project.horizon_from,
                           tod=project.horizon_to, step=units.MONTHLY, times=1), 
        "Loss 3"],     
        [Table(name="Width Table", columns=[Column(name="Flow Through Link", unit="m3/s"),
                                           Column(name="Width", unit="m")]), "Loss 2"],
     
        [Parameter(name="Base Width", unit='m'), "Loss 3"],
        [Parameter(name="Channel Depth", unit='m'), "Loss 3"],
        [Parameter(name="Lower Right Slope", unit='m'), "Loss 3"],
        [Parameter(name="Upper Right Slope", unit='m'), "Loss 3"],
        [Parameter(name="Lower Left Slope", unit='m'), "Loss 3"],
        [Parameter(name="Upper Left Slope", unit='m'), "Loss 3"],
        [Parameter(name="Power Capacity", unit='kW'), "Power"],
        [Parameter(name="Operating Time Factor", unit='-'), "Power"],
        [Parameter(name="Turbine Elevation", unit='m'), "Power"],
        [Parameter(name="Power Efficiency", unit='-'), "Power"],
        [Parameter(name="Min Flow", unit='m3/s'), "Power"],
        [Parameter(name="Pump Efficiency", unit='-'), "Power"],
        Parameter(name="Hydropower Enabled?", unit='-', format="binary"),
        Parameter(name="Pumping Enabled?", unit='-', format="binary"),
        Parameter(name="Loss Method", unit='-', format="integer"),
        Parameter(name="Routing Method", unit='-', format="integer"),
        Table(name="Allocation Function", 
               columns=[Column(name="Total Flow from Upstream Node", unit="m3/s"),
                        Column(name="Total Allocated to Link", unit="m3/s")]),
        Parameter(name="Time-Step Flow Capacity", unit='m3/s'),
        Parameter(name="Annual Flow Capacity", unit='m3/s'),
    ]
    
    
    unidirectional_link_fields = [
		Parameter(name="Price per kWh", unit='-'),
    	[Parameter(name="Initial Storage 1", unit='hm3'), 
        "Routing 1"],
        [Parameter(name="Initial Storage 2", unit='hm3'), 
        "Routing 2"],
        [Parameter(name="Number of Cascading Reservoirs", unit='-', format="integer"), 
        "Routing 2"],
        [Parameter(name="a (inflow parameter)", unit='-'), "Routing 2"],
        [Parameter(name="b (volume parameter)", unit='-'), "Routing 2"],
        [Parameter(name="c (exponent)", unit='-'), "Routing 2"],
        [Parameter(name="Detention Storage", unit='hm3'), 
        "Routing 1"], 
        [Parameter(name="Linear Parameter", unit='-'), 
        "Routing 1"],  
        [Parameter(name="Exponent", unit='-'), 
        "Routing 1"],  
        [Table(name="Loss Table", columns=[Column(name="Flow Through Link", unit="m3/s"),
                                           Column(name="Loss", unit="m3/s")]), "Loss 1"],
        [Parameter(name="Length 2", unit='m'), 
        "Loss 2"],
        [Parameter(name="Length 3", unit='m'), 
        "Loss 3"],  
        Parameter(name="kWh per m3", unit='-'),
        [SeasonalParameter(name="Loss Rate 2", unit='m/day', 
                           fromd=project.horizon_from, 
                           tod=project.horizon_to, step=units.MONTHLY, times=1), 
        "Loss 2"],    
        [SeasonalParameter(name="Loss Rate 3", unit='m/day', 
                           fromd=project.horizon_from, 
                           tod=project.horizon_to, step=units.MONTHLY, times=1), 
        "Loss 3"],     

        [Table(name="Width Table", columns=[Column(name="Flow Through Link", unit="m3/s"),
                                           Column(name="Width", unit="m")]), "Loss 2"],
     
        [Parameter(name="Base Width", unit='m'), "Loss 3"],
        [Parameter(name="Channel Depth", unit='m'), "Loss 3"],
        [Parameter(name="Lower Right Slope", unit='m'), "Loss 3"],
        [Parameter(name="Upper Right Slope", unit='m'), "Loss 3"],
        [Parameter(name="Lower Left Slope", unit='m'), "Loss 3"],
        [Parameter(name="Upper Left Slope", unit='m'), "Loss 3"],
        [Parameter(name="Power Capacity", unit='kW'), "Power"],
        [Parameter(name="Operating Time Factor", unit='-'), "Power"],
        [Parameter(name="Turbine Elevation", unit='m'), "Power"],
        [Parameter(name="Power Efficiency", unit='-'), "Power"],
        [Parameter(name="Min Flow", unit='m3/s'), "Power"],
        [Parameter(name="Pump Efficiency", unit='-'), "Power"],
        Parameter(name="Hydropower Enabled?", unit='-', format="binary"),
        Parameter(name="Pumping Enabled?", unit='-', format="binary"),
        Parameter(name="Loss Method", unit='-', format="integer"),
        Parameter(name="Routing Method", unit='-', format="integer"),
        Table(name="Allocation Function", 
               columns=[Column(name="Total Flow from Upstream Node", unit="m3/s"),
                        Column(name="Total Allocated to Link", unit="m3/s")]),
    ]
    
    groundwater_link_fields = [
        Parameter(name="Length", unit='m'),
        Parameter(name="Link Elevation", unit='m'),
        Parameter(name="Width", unit='m'),
        Parameter(name="K", unit='m/day'),
        Parameter(name="GW flow Computation Method", unit='-', format="integer"),
        [Table(name="GW Flow Matrix", columns=[Column(name="From Node Storage or Flow", unit="-"),
                                               Column(name="To Node Storage or Flow", unit="-"),
                                               Column(name="Flow", unit="m3/s")]), "Flow Matrix Flow Method"],
        [Parameter(name="Pump Efficiency", unit='-'), "Power"],
        Parameter(name="Link Area", unit='sqm'),
        Parameter(name="Pumping Enabled?", unit='-', format="binary"),
    ]
    
    surface_bidirectional_link_fields = [
        [Table(name="Flow Matrix", columns=[Column(name="From Node Storage or Flow", unit="-"),
                                               Column(name="To Node Storage or Flow", unit="-"),
                                               Column(name="Flow", unit="m3/s")]), "Flow Matrix Flow Method"],
        [Parameter(name="Power Capacity", unit='kW'), "Power"],
        [Parameter(name="Operating Time Factor", unit='-'), "Power"],
        [Parameter(name="Turbine Elevation", unit='m'), "Power"],
        [Parameter(name="Power Efficiency", unit='-'), "Power"],
        [Parameter(name="Min Flow", unit='m3/s'), "Power"],
        [Parameter(name="Pump Efficiency", unit='-'), "Power"],
        Parameter(name="Hydropower Enabled?", unit='-', format="binary"),
        Parameter(name="Pumping Enabled?", unit='-', format="binary"),
    ]
    
    diversion_link_fields = [
 		Parameter(name="Price per kWh", unit='-'), 
       [Parameter(name="Initial Storage 1", unit='hm3'), 
        "Routing 1"],
        [Parameter(name="Initial Storage 2", unit='hm3'), 
        "Routing 2"],
        [Parameter(name="Number of Cascading Reservoirs", unit='-', format="integer"), 
        "Routing 2"],
        [Parameter(name="a (inflow parameter)", unit='-'), "Routing 2"],
        [Parameter(name="b (volume parameter)", unit='-'), "Routing 2"],
        [Parameter(name="c (exponent)", unit='-'), "Routing 2"],
        [Parameter(name="Detention Storage", unit='hm3'), 
        "Routing 1"], 
        [Parameter(name="Linear Parameter", unit='-'), 
        "Routing 1"],  
        [Parameter(name="Exponent", unit='-'), 
        "Routing 1"],  
        [Table(name="Loss Table", columns=[Column(name="Flow Through Link", unit="m3/s"),
                                           Column(name="Loss", unit="m3/s")]), "Loss 1"], 
       
	   [Parameter(name="Length 2", unit='m'), 
        "Loss 2"],
        [Parameter(name="Length 3", unit='m'), 
        "Loss 3"],  
        Parameter(name="kWh per m3", unit='-'),
        [SeasonalParameter(name="Loss Rate 2", unit='m/day', 
                           fromd=project.horizon_from, 
                           tod=project.horizon_to, step=units.MONTHLY, times=1), 
        "Loss 2"],    
        [SeasonalParameter(name="Loss Rate 3", unit='m/day', 
                           fromd=project.horizon_from, 
                           tod=project.horizon_to, 
                           step=units.MONTHLY, times=1), 
        "Loss 3"],     
        [Table(name="Width Table", columns=[Column(name="Flow Through Link", unit="m3/s"),
                                           Column(name="Width", unit="m")]), "Loss 2"],
     
        [Parameter(name="Base Width", unit='m'), "Loss 3"],
        [Parameter(name="Channel Depth", unit='m'), "Loss 3"],
        [Parameter(name="Lower Right Slope", unit='m'), "Loss 3"],
        [Parameter(name="Upper Right Slope", unit='m'), "Loss 3"],
        [Parameter(name="Lower Left Slope", unit='m'), "Loss 3"],
        [Parameter(name="Upper Left Slope", unit='m'), "Loss 3"],
        [Parameter(name="Power Capacity", unit='kW'), "Power"],
        [Parameter(name="Operating Time Factor", unit='-'), "Power"],
        [Parameter(name="Turbine Elevation", unit='m'), "Power"],
        [Parameter(name="Power Efficiency", unit='-'), "Power"],
        [Parameter(name="Min Flow", unit='m3/s'), "Power"],
        [Parameter(name="Pump Efficiency", unit='-'), "Power"],
        Parameter(name="Hydropower Enabled?", unit='-', format="binary"),
        Parameter(name="Pumping Enabled?", unit='-', format="binary"),
        Parameter(name="Loss Method", unit='-', format="integer"),
        Parameter(name="Routing Method", unit='-', format="integer"),
        Table(name="Allocation Function", 
               columns=[Column(name="Total Flow from Upstream Node", unit="m3/s"),
                        Column(name="Total Allocated to Link", unit="m3/s")]),
        Parameter(name="Time-Step Flow Capacity", unit='m3/s'),
        Parameter(name="Annual Flow Capacity", unit='m3/s'),
    ]
    
    link_types = [
        [u"Demand/Diversion Link", demand_diversion_link_description,
         u"#8000ff", u"wx.SOLID", 1, demand_diversion_link_fields],
        [u"Unidirectional Link", unidirectional_link_description,
         u"black", u"wx.SOLID", 1, unidirectional_link_fields],
        [u"Demand Link", demand_link_description,
         u"#ff0000", u"wx.SOLID", 1, demand_diversion_link_fields],
        [u"Groundwater Link", groundwater_link_description,
         u"black", u"wx.SOLID", 1, groundwater_link_fields],
        [u"Surface Bidirectional Link", surface_bidirectional_link_description, 
         "#808000", u"wx.SOLID", 1, surface_bidirectional_link_fields],
        [u"Diversion Link", diversion_link_description, 
         u"#ffff00", u"wx.SOLID", 1, diversion_link_fields]
    ] 
    
    return link_key_names, link_types